Next: Fields, Previous: Lazy Properties, Up: Text Properties [Contents][Index]
Clickable text is text that can be clicked, with either the mouse or via a keyboard command, to produce some result. Many major modes use clickable text to implement textual hyper-links, or links for short.
The easiest way to insert and manipulate links is to use the
button package. See Buttons. In this section, we will
explain how to manually set up clickable text in a buffer, using
text properties. For simplicity, we will refer to the clickable
text as a link.
Implementing a link involves three separate steps: (1)
indicating clickability when the mouse moves over the link; (2)
making RET or mouse-2 on that
link do something; and (3) setting up a follow-link
condition so that the link obeys
mouse-1-click-follows-link.
To indicate clickability, add the mouse-face text
property to the text of the link; then Emacs will highlight the
link when the mouse moves over it. In addition, you should define
a tooltip or echo area message, using the help-echo
text property. See Special
Properties. For instance, here is how Dired indicates that
file names are clickable:
(if (dired-move-to-filename)
(add-text-properties
(point)
(save-excursion
(dired-move-to-end-of-filename)
(point))
'(mouse-face highlight
help-echo "mouse-2: visit this file in other window")))
To make the link clickable, bind RET and mouse-2 to commands that perform the desired action. Each command should check to see whether it was called on a link, and act accordingly. For instance, Dired’s major mode keymap binds mouse-2 to the following command:
(defun dired-mouse-find-file-other-window (event)
"In Dired, visit the file or directory name you click on."
(interactive "e")
(let ((window (posn-window (event-end event)))
(pos (posn-point (event-end event)))
file)
(if (not (windowp window))
(error "No file chosen"))
(with-current-buffer (window-buffer window)
(goto-char pos)
(setq file (dired-get-file-for-visit)))
(if (file-directory-p file)
(or (and (cdr dired-subdir-alist)
(dired-goto-subdir file))
(progn
(select-window window)
(dired-other-window file)))
(select-window window)
(find-file-other-window (file-name-sans-versions file t)))))
This command uses the functions posn-window and
posn-point to determine where the click occurred,
and dired-get-file-for-visit to determine which file
to visit.
Instead of binding the mouse command in a major mode keymap,
you can bind it within the link text, using the
keymap text property (see Special
Properties). For instance:
(let ((map (make-sparse-keymap))) (define-key map [mouse-2] 'operate-this-button) (put-text-property link-start link-end 'keymap map))
With this method, you can easily define different commands for different links. Furthermore, the global definition of RET and mouse-2 remain available for the rest of the text in the buffer.
The basic Emacs command for clicking on links is
mouse-2. However, for compatibility with other
graphical applications, Emacs also recognizes mouse-1
clicks on links, provided the user clicks on the link quickly
without moving the mouse. This behavior is controlled by the user
option mouse-1-click-follows-link. See
Mouse References in The GNU Emacs
Manual.
To set up the link so that it obeys
mouse-1-click-follows-link, you must either (1)
apply a follow-link text or overlay property to the
link text, or (2) bind the follow-link event to a
keymap (which can be a major mode keymap or a local keymap
specified via the keymap text property). The value
of the follow-link property, or the binding for the
follow-link event, acts as a condition for the link
action. This condition tells Emacs two things: the circumstances
under which a mouse-1 click should be regarded as
occurring inside the link, and how to compute an action code that
says what to translate the mouse-1 click into. The
link action condition can be one of the following:
mouse-faceIf the condition is the symbol mouse-face, a
position is inside a link if there is a non-nil
mouse-face property at that position. The action
code is always t.
For example, here is how Info mode handles mouse-1:
(define-key Info-mode-map [follow-link] 'mouse-face)
If the condition is a function, func, then a
position pos is inside a link if
(func pos) evaluates to
non-nil. The value returned by func
serves as the action code.
For example, here is how pcvs enables mouse-1 to follow links on file names only:
(define-key map [follow-link]
(lambda (pos)
(eq (get-char-property pos 'face) 'cvs-filename-face)))
If the condition value is anything else, then the position is inside a link and the condition itself is the action code. Clearly, you should specify this kind of condition only when applying the condition via a text or property overlay on the link text (so that it does not apply to the entire buffer).
The action code tells mouse-1 how to follow the link:
If the action code is a string or vector, the
mouse-1 event is translated into the first element
of the string or vector; i.e., the action of the
mouse-1 click is the local or global binding of
that character or symbol. Thus, if the action code is
"foo", mouse-1 translates into
f. If it is [foo], mouse-1
translates into foo.
For any other non-nil action code, the
mouse-1 event is translated into a
mouse-2 event at the same position.
To define mouse-1 to activate a button defined with
define-button-type, give the button a
follow-link property. The property value should be a
link action condition, as described above. See Buttons. For example, here is how Help
mode handles mouse-1:
(define-button-type 'help-xref 'follow-link t 'action #'help-button-action)
To define mouse-1 on a widget defined with
define-widget, give the widget a
:follow-link property. The property value should be
a link action condition, as described above. For example, here is
how the link widget specifies that a mouse-1 click shall be translated to RET:
(define-widget 'link 'item "An embedded link." :button-prefix 'widget-link-prefix :button-suffix 'widget-link-suffix :follow-link "\C-m" :help-echo "Follow the link." :format "%[%t%]")
This function returns non-nil if position
pos in the current buffer is on a link.
pos can also be a mouse event location, as
returned by event-start (see Accessing
Mouse).
Next: Fields, Previous: Lazy Properties, Up: Text Properties [Contents][Index]